package dfsketches.mineral_generator;

import java.util.Random;

public class FlatMineralGenerator implements MineralGenerator{
	
	public FlatMineralGenerator(int[][] bitmap) {
		this.bitmap = bitmap;
	}
	
	public void test() {
		BoundingBox box = new BoundingBox(0, 0, 0, bitmap.length, bitmap[0].length, 100);
		for (int i = 0; i < bitmap.length; i++)
			for (int j = 0; j < bitmap[0].length; j++)
				bitmap[i][j] = 0xFF000000;
		addVein(10, 20, 3, box, 0xFF0000FF);
		addVein(10, 20, 3, box, 0xFFFF0000);
		addVein(10, 20, 3, box, 0xFFFF0000);
		addVein(10, 20, 3, box, 0xFFFF0000);
		addVein(10, 20, 3, box, 0xFFFF0000);
		addVein(10, 20, 3, box, 0xFFFF0000);
		addVein(10, 20, 3, box, 0xFFFFFFFF);
		addVein(10, 20, 3, box, 0xFFFF00FF);
		addVein(10, 20, 3, box, 0xFF7F007F);
		addVein(10, 20, 3, box, 0xFF00FF00);
		addVein(10, 20, 3, box, 0xFF007F00);
		addVein(10, 20, 3, box, 0xFF00FF00);
		addVein(10, 20, 3, box, 0xFF00007F);
		addVein(10, 20, 3, box, 0xFF007F7F);
		addVein(10, 20, 3, box, 0xFF3FFF7F);
		addVein(10, 20, 3, box, 0xFF00FFFF);
		addCluster(5, 1000, 50, box, 0xFF7F7F7F);
		addCluster(3, 1000, 50, box, 0xFF7F7F00);
		addPlacer(3f, 0.5f, box, 0xFF00007F);
		addPlacer(3f, 0.5f, box, 0xFF00007F);
		addPlacer(3f, 0.5f, box, 0xFF00007F);
		addPlacer(3f, 0.5f, box, 0xFF00007F);
		addPlacer(3f, 0.5f, box, 0xFF00007F);
		addPlacer(3f, 0.5f, box, 0xFF00007F);
	}
	
	/**
	 * Добавляет жилу на основе кривой Безье.
	 * @param len Количество узлов у кривой Безье.
	 * @param scale Максимальное расстояние между узлами (в плане кубической метрики).
	 * @param wide Толщина самой жилы.
	 * @param box Ограничивающая коробка, что ещё сказать. z-координата роли не играет.
	 * @param color Каким числом заполнить массив в тех местах, где жила проходит.
	 */
	public void addVein(int len, float scale, float wide, BoundingBox box, int color) {
		int[] c = new int[len];
		c[0] = 1;
		for (int i = 1; i < len; i++)
			for (int j = len - 1; j > 0; j--)
				c[j] = c[j] + c[j - 1];
		
		int bound = (int)wide * 2;
		wide *= wide;
		
		int w = box.w();
		int h = box.h();

		Random rand = new Random();
		float[] x = new float[len];
		float[] y = new float[len];
		x[0] = rand.nextFloat() * w + box.x();
		y[0] = rand.nextFloat() * h + box.y();
		for (int k = 1; k < len; k++) {
			x[k] = x[k - 1] + rand.nextFloat() * scale - scale / 2;
			y[k] = y[k - 1] + rand.nextFloat() * scale - scale / 2;
		}
		
		float[][] psi = new float[w][h];
		for (int i = 0; i < w; i++)
			for (int j = 0; j < h; j++)
				psi[i][j] = 0;
		
		float delta = 1 / scale / len;
		for (float t = 0; t < 1 - delta/2; t += delta) {
			float xt = 0;
			float yt = 0;
			float pow1 = 1;
			float pow2 = fastPower(1 - t, len - 1);
			float div = 1 / (1-t);
			float m;
			for (int k = 0; k < len; k++) {
				m = c[k] * pow1 * pow2;
				xt += m * x[k];
				yt += m * y[k];
				pow1 *= t;
				pow2 *= div;
			}
			
			int rbound = Math.max((int)xt - bound, 0);
			int lbound = Math.min((int)xt + bound, w);
			int ubound = Math.max((int)yt - bound, 0);
			int bbound = Math.min((int)yt + bound, h);
			for (int i = rbound; i < lbound; i++) {
				float r0 = (xt - i) * (xt - i) + (yt - ubound) * (yt - ubound);
				for (int j = ubound; j < bbound; j++) {
					psi[i][j] = Math.max(psi[i][j], (float)Math.exp(-r0*r0 / wide));
					r0 = r0 + 2 * j - 2 * yt + 1;
				}
			}
		}
		
		for (int i = box.x(); i < w; i++)
			for (int j = box.y(); j < h; j++)
				if (rand.nextFloat() <= psi[i][j]) bitmap[i][j] = color; 
	}
	
	public static float fastPower(float v, int p) {
		float temp;
		if (p > 0) {
			temp = fastPower(v, p / 2);
			if (p % 2 == 0) {
				return temp * temp;
			} else {
				return temp * temp * v;
			}
		}
		return 1;
	}

	public void addCluster(int complexity, float wide, float dist, BoundingBox box, int color) {
		Random rand = new Random();
		float[] x = new float[complexity];
		float[] y = new float[complexity];
		x[0] = rand.nextFloat() * box.w() + box.x();
		y[0] = rand.nextFloat() * box.h() + box.y();
		for (int k = 1; k < complexity; k++) {
			x[k] = x[k - 1] + rand.nextFloat() * dist - dist / 2;
			y[k] = y[k - 1] + rand.nextFloat() * dist - dist / 2;
		}

		float[][] psi = new float[box.w()][ box.h()];
		for (int i = 0; i < box.w(); i++)
			for (int j = 0; j < box.h(); j++)
				psi[i][j] = 1;
		
		for (int k = 0; k < complexity; k++) {
			float dx = x[k];
			for (int i = 0; i < box.w(); i++) {
				float dy = y[k];
				for (int j = 0; j < box.h(); j++) {
					psi[i][j] *= (dx * dx + dy * dy) / wide;
					dy -= 1f;
				}
				dx -= 1f;
			}
		}
		
		for (int i = 0; i < box.w(); i++)
			for (int j = 0; j < box.h(); j++)
				if (rand.nextFloat() <= 1f - Math.exp(-1 / fastPower(psi[i][j], 15))) bitmap[i][j] = color;
	}
	
	public void addSpot(int complexity, float wide, BoundingBox box, int color) {
		Random rand = new Random();
		float[] x = new float[complexity];
		float[] y = new float[complexity];
		for (int k = 0; k < complexity; k++) {
			x[k] = rand.nextFloat() * box.w() * 0.6f + box.w() * 0.2f;
			y[k] = rand.nextFloat() * box.h() * 0.6f + box.h() * 0.2f;
		}

		float[][] psi = new float[box.w()][ box.h()];
		for (int i = 0; i < box.w(); i++)
			for (int j = 0; j < box.h(); j++)
				psi[i][j] = 1;
		
		for (int k = 0; k < complexity; k++) {
			float dx = x[k];
			for (int i = 0; i < box.w(); i++) {
				float dy = y[k];
				for (int j = 0; j < box.h(); j++) {
					psi[i][j] *= (dx * dx + dy * dy) / wide;
					dy -= 1f;
				}
				dx -= 1f;
			}
		}
		
		for (int i = 0; i < box.w(); i++)
			for (int j = 0; j < box.h(); j++)
				if (rand.nextFloat() <= 1f - Math.exp(-1 / fastPower(psi[i][j], 15) / complexity)) bitmap[i][j] = color;
	}
	
	/**
	 * Adds cluster which whole contained in box.
	 */
	public void addFullCluster(int complexity, float wide, float dist, BoundingBox box, int color) {
		Random rand = new Random();
		float[] x = new float[complexity];
		float[] y = new float[complexity];
		int shift = (int) (Math.max(Math.floor(Math.sqrt(wide)), dist));
		int w = box.w() - shift * 2;
		int h = box.h() - shift * 2;
		x[0] = rand.nextFloat() * w + box.x() + shift;
		y[0] = rand.nextFloat() * h + box.y() + shift;
		for (int k = 1; k < complexity; k++) {
			x[k] = x[k - 1] + rand.nextFloat() * dist - dist / 2;
			y[k] = y[k - 1] + rand.nextFloat() * dist - dist / 2;
			if (x[k] < shift) x[k] += shift;
			if (y[k] < shift) y[k] += shift;
			if (x[k] > box.w() - shift) x[k] -= shift;
			if (y[k] > box.h() - shift) y[k] -= shift;
		}

		float[][] psi = new float[box.w()][ box.h()];
		for (int i = 0; i < box.w(); i++)
			for (int j = 0; j < box.h(); j++)
				psi[i][j] = 1;
		
		for (int k = 0; k < complexity; k++) {
			float dx = x[k];
			for (int i = 0; i < box.w(); i++) {
				float dy = y[k];
				for (int j = 0; j < box.h(); j++) {
					psi[i][j] *= (dx * dx + dy * dy) / wide;
					dy -= 1f;
				}
				dx -= 1f;
			}
		}
		
		for (int i = 0; i < box.w(); i++)
			for (int j = 0; j < box.h(); j++)
				if (rand.nextFloat() <= 1f - Math.exp(-1 / fastPower(psi[i][j], 15))) bitmap[i][j] = color;
	}
	
	public void addPlacer(float radius, float probability, BoundingBox box, int color) {
		Random rand = new Random();
		float x = rand.nextFloat() * box.w() + box.x();
		float y = rand.nextFloat() * box.h() + box.y();
		
		int rbound = (int) Math.max(x - radius, 0f);
		int lbound = (int) Math.min(x + radius, (float)box.w());
		int ubound = (int) Math.max(y - radius, 0f);
		int bbound = (int) Math.min(y + radius, (float)box.h());

		for (int i = rbound; i < lbound; i++)
			for (int j = ubound; j < bbound; j++)
				if (rand.nextFloat() <= probability) bitmap[i][j] = color;
		
	}
	
	private int[][] bitmap;

}
